package game;

import java.awt.Point;
import java.util.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import xml.XMLcommon;
import xml.XMLizeable;

/**
 * <p>The Knight's Tour is a unique game where one <tt>Knight</tt> begins at an arbitrary location on the board and moves 
 * around the board touching each square only once. Once all the squares have been touched only once the game is complete.</p>
 * 
 * @author Nicole Waldrum
 * @date January 30, 2010
 * @version 1.0
 * 
 * @author Mark Kelly
 * @date February 20, 2010
 * @version 2.0
 * 
 * @author Mark Kelly
 * @date March 9, 2010
 * @version 3.0
 * 
 * @author Trevor Bastian
 * @date March 29, 2010
 * @version 4.0
 *
 */
public class KnightsTour extends GameType implements XMLizeable {
	private static final Team KNIGHT_COLOUR = Team.BLACK;
	// InstanceVariables
    private Set<Point> usedSquares;
    
    /**
	 * <p>Creates a new instance of <tt>KnightTour</tt>.</p>
	 */
	public KnightsTour() {
		super();
		usedSquares = new HashSet<Point>();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * <p>In the case of the Knight's tour, the new position of the <tt>Knight</tt> is added to the 'blacklist' of spaces that the <tt>Knight</tt> has used.</p>
	 */
	public void pieceMoved (GameState gs, Piece p, ChessBoard b) {
		usedSquares.add(p.getLocation());
		
		if (hasWon(p.getTeam())) {
			state.stateChanged(state);
			state.notifyGameWon(p.getTeam());
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * <p>In the case of the Knight's Tour, a single <tt>Knight</tt> is placed on the board.</p>
	 */
	@Override
	public void setupBoard() {
		ChessBoard board = new ChessBoard(boardSize);
		
		Point start = new Point (1, 1);

		board.addPiece(new Knight(start, KNIGHT_COLOUR));
		
		usedSquares.add(start);
		
		state.setBoard(board);
	}
	
	/**
	 * {@inheritDoc}
	 * 
	 * <p>In the case of the Knight's Tour, each square must have been visited once.</p>
	 */
	@Override
	public boolean hasWon(Team team) {
		return (usedSquares.size() == 64);
	}
	
	/**
	 * {@inheritDoc}
	 * 
	 * <p>In the case of the Knight's Tour, the square cannot have been previously visited by the <tt>Knight</tt>.</p>
	 */
	public boolean isValidMove(Piece piece, Point dest){
		return !(usedSquares.contains(dest));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean takeTurn(Point start, Point dest) {
		ChessBoard board = state.getBoard();

		Knight knight = (Knight)state.getBoard().getPieceList(Knight.class, KNIGHT_COLOUR).get(0);
		
		// this will leave a trail of Knights of the opposite colour
		Knight oldKnight = new Knight(knight.getLocation(), knight.getTeam().other());
		
		// check if its a valid move and if the pawn exception is in effect
		if (state.isValidMove(knight, dest)) {
			knight = new Knight(dest, knight.getTeam());
			board.movePiece(knight, dest);
			board.movePiece(oldKnight, oldKnight.getLocation());
			state.notifyPieceMoved(knight, board);
			return true;
		}

		// invalid move
		return false;
	}

	@Override
	public void needPieceChoice(GameState gs) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * Private constructor which takes an XML element as an argument
	 * @param elem an XML representation of KnightTour
	 */
	private KnightsTour (Element elem) {
		super();
		usedSquares = new HashSet<Point>();
		
		NodeList points = elem.getElementsByTagName(Point.class.getName());
		for (int i = 0; i < points.getLength(); i++) {
			usedSquares.add(XMLcommon.parseXMLPoint((Element)points.item(i)));
		}
	}
	
	/**
	 * Returns an initialized instance of KnightTour. Initialized to the specs in the XML element elem
	 * @param elem XML representation of KnightTour
	 * @return initialized instance of KnightTour
	 */
	public static KnightsTour importFromXML(Element elem) {
		return new KnightsTour (elem);
	}
	
	@Override
	public Element toXML(Document doc) {
		Element element;
		Element gameType = doc.createElement("GameType");
		gameType.setAttribute("GameType", KnightsTour.class.getName());
		
		for (Point p: this.usedSquares) {
			element = XMLcommon.pointToXML(doc, p);
			gameType.appendChild(element);
		}
		
		return gameType;
	}
}
